/*
*  SkyDefaultOpenALContext.cpp
*  
*
*  Created by Alexander Trutchenko on 11/02/12.
*  Copyright 2012 Free Team. All rights reserved.
*
*/

#include "SkyDefaultOpenALContext.h"

#include "SkyOpenALBuffer.h"
#include "SkySoundSource.h"
#include "SkyBufferManager.h"
#include "SkyAssert.h"
#include "SkyOpenALInclude.h"
#include "SkySoundEngine.h"

namespace sky {

////////////////////////////////////////////////////////
//
/** SkyDefaultOpenALContext
*  
*/
////////////////////////////////////////////////////////

float SkyDefaultOpenALContext::s_effectsVolume = 1.f;
float SkyDefaultOpenALContext::s_bkMusicVolume = 1.f;
//bool  SkyDefaultOpenALContext::s_effectsMute = false;
//bool SkyDefaultOpenALContext::s_bkMusicMute = false;

SkyDefaultOpenALContext::SkyDefaultOpenALContext(unsigned int maxSources)
: _maxSources(maxSources)
, _sourceCursor(0)
, _effectsVolume(1.f)
, _bkMusicVolume(1.f)
{
	SkyAssert(maxSources > 0, "SkyDefaultOpenALContext::SkyDefaultOpenALContext: wrong sources count!");
}

SkyDefaultOpenALContext::~SkyDefaultOpenALContext()
{
}

////////////////////////////////////////////////////////
// SkyOpenALContext functions
void SkyDefaultOpenALContext::setIsCurrent(bool isCurrent)
{
	SkyOpenALContext::setIsCurrent(isCurrent);

	if (!_isCurrent)
		return;

	if (fabs(_effectsVolume - s_effectsVolume) > FLT_EPSILON)
	{
		_effectsVolume = s_effectsVolume;
		for (SkyEffects::const_iterator it = _effects.begin(); it != _effects.end(); ++it)
			(*it)->setGain(((SkySSTagDefault*)(*it)->tag())->_realGain*_effectsVolume);
	}
	if (fabs(_bkMusicVolume - s_bkMusicVolume) > FLT_EPSILON)
	{
		_bkMusicVolume = s_bkMusicVolume;
		for (SkyBkMusic::const_iterator it = _bkMusic.begin(); it != _bkMusic.end(); ++it)
			(*it)->setGain(((SkySSTagDefault*)(*it)->tag())->_realGain*_bkMusicVolume);
	}
}

////////////////////////////////////////////////////////
// work with sounds
		/** Returns effect ID*/
int SkyDefaultOpenALContext::playEffect(const char* file, bool loop, float gain, float pitch)
{
	if (!_isCurrent)
		return SKY_INVALID_ID;

	SkyOpenALBuffer* fileBuffer = _bufferManager->bufferForFile(file);
	
	//Check sources with this buffer
	std::pair<SkyConnectedEffects::const_iterator, SkyConnectedEffects::const_iterator> ret = _connectedEffects.equal_range(fileBuffer->bufferID());
	for (SkyConnectedEffects::const_iterator it = ret.first; it != ret.second; ++it)
	{
		int state = it->second->getSourceState();
		if (state == AL_INITIAL || state == AL_STOPPED)
		{
			SkySoundSourceStatic* source = it->second;
			source->setSoundID(generateSoundID());
			((SkySSTagDefault*)source->tag())->_realGain = gain;
			source->setGain(_effectsVolume*gain);
			source->setPitch(pitch);
			source->play();
			source->setIsLooping(loop);
			return source->soundID();
		}
	}

	//Check any unplayed sources
	unsigned int count = _effects.size();
	for (unsigned int i = 0; i < count; ++i)
	{
		if (_sourceCursor >= count)
			_sourceCursor = 0;
		int sourceCursor = _sourceCursor;
		++_sourceCursor;
		int state = _effects[sourceCursor]->getSourceState();
		if (state == AL_INITIAL || state == AL_STOPPED)
			return playStaticSound(_effects[sourceCursor], fileBuffer, loop, gain, pitch);
	}

	//Create new sound source
	if (count < _maxSources)
	{
		SkySoundSourceStatic* source = new SkySoundSourceStatic(this, _bufferManager, new SkySSTagDefault(gain));
		if (!source->isValid())
			return SKY_INVALID_ID;
		_effects.push_back(source);
		//Create new link
		_connectedEffects.insert(SkyConnectedEffects::value_type(fileBuffer->bufferID(), source));
		//Play sound
		addSource(source);
		source->setBuffer(fileBuffer);
		source->setGain(_effectsVolume*gain);
		source->setPitch(pitch);
		source->setIsLooping(loop);
		source->play();
		return source->soundID();
	}

	//Stop oldest unlooping effect
	for (unsigned int i = 0; i < count; ++i)
	{
		if (_sourceCursor >= count)
			_sourceCursor = 0;
		int sourceCursor = _sourceCursor;
		++_sourceCursor;
		if (!_effects[_sourceCursor]->isLooping())
		{
			_effects[sourceCursor]->stop();
			return playStaticSound(_effects[sourceCursor], fileBuffer, loop, gain, pitch);
		}
	}
	//all sounds are looping
	return SKY_INVALID_ID;
}

void SkyDefaultOpenALContext::stopEffect(int soundID)
{
	if (!_isCurrent)
		return;

	for (SkyEffects::const_iterator it = _effects.begin(); it != _effects.end(); ++it)
		if ((*it)->soundID() == soundID)
		{
			(*it)->stop();
			break;
		}
}

void SkyDefaultOpenALContext::setEffectsVolume(float volume)
{
	if (volume < 0.f)
		volume = 0.f;
	else if (volume > 1.f)
		volume = 1.f;
	if (fabs(volume - s_effectsVolume) < FLT_EPSILON)
		return;
	s_effectsVolume = volume;

	SkyDefaultOpenALContext* _currentContext = dynamic_cast<SkyDefaultOpenALContext*>(SkySoundEngine::sharedEngine()->currentContext());
	if (_currentContext != NULL)
	{
		_currentContext->_effectsVolume = s_effectsVolume;
		for (SkyEffects::const_iterator it = _currentContext->_effects.begin(); it != _currentContext->_effects.end(); ++it)
			(*it)->setGain(((SkySSTagDefault*)(*it)->tag())->_realGain*s_effectsVolume);
	}
}
/*
void SkyDefaultOpenALContext::setIsEffectsMute(bool isMuted)
{
	if (s_effectsMute == isMuted)
		return;
	s_effectsMute == isMuted;

	SkyDefaultOpenALContext* _currentContext = dynamic_cast<SkyDefaultOpenALContext*>(SkySoundEngine::sharedEngine()->currentContext());
	if (_currentContext != NULL)
	{
	}
}
void SkyDefaultOpenALContext::setIsBkMusicMute(bool isMuted)
{
}
*/

void SkyDefaultOpenALContext::stopAllEffects()
{
	for (SkyEffects::const_iterator it = _effects.begin(); it != _effects.end(); ++it)
		(*it)->stop();
}

void SkyDefaultOpenALContext::pauseAllEffects()
{
	for (SkyEffects::const_iterator it = _effects.begin(); it != _effects.end(); ++it)
		(*it)->pause();
}

void SkyDefaultOpenALContext::resumeAllEffects()
{
	for (SkyEffects::const_iterator it = _effects.begin(); it != _effects.end(); ++it)
	{
		int state = (*it)->getSourceState();
		if (state == AL_PAUSED)
			(*it)->play();
	}
}


//work with streamed sounds - background music
	/** Returns sound ID and SKY_INVALID_ID if sound can't be played */
int SkyDefaultOpenALContext::playBkMusic(const char* file, bool loop, float gain, float pitch)
{
	if (!_isCurrent)
		return SKY_INVALID_ID;

	SkySoundSourceStreaming* bk = new SkySoundSourceStreaming(this, _bufferManager, new SkySSTagDefault(gain));
	if (!bk->isValid() || !bk->setFile(file))
	{
		delete bk;
		return SKY_INVALID_ID;
	}
	addSource(bk);
	bk->setIsLooping(loop);
	bk->setGain(gain*s_bkMusicVolume);
	bk->setPitch(pitch);
	bk->play();
	_bkMusic.push_back(bk);

	return bk->soundID();
}

void SkyDefaultOpenALContext::stopBkMusic(int soundID)
{
	for (SkyBkMusic::const_iterator it = _bkMusic.begin(); it != _bkMusic.end(); ++it)
		if ((*it)->soundID() == soundID)
		{
			(*it)->stop();
			break;
		}
}
void SkyDefaultOpenALContext::setBkMusicVolume(float volume)
{
	if (volume < 0.f)
		volume = 0.f;
	else if (volume > 1.f)
		volume = 1.f;
	if (fabs(volume - s_bkMusicVolume) < FLT_EPSILON)
		return;
	s_bkMusicVolume = volume;

	SkyDefaultOpenALContext* _currentContext = dynamic_cast<SkyDefaultOpenALContext*>(SkySoundEngine::sharedEngine()->currentContext());
	if (_currentContext != NULL)
	{
		_currentContext->_bkMusicVolume = s_bkMusicVolume;
		for (SkyBkMusic::const_iterator it = _currentContext->_bkMusic.begin(); it != _currentContext->_bkMusic.end(); ++it)
			(*it)->setGain(((SkySSTagDefault*)(*it)->tag())->_realGain*s_bkMusicVolume);
	}
}
void SkyDefaultOpenALContext::stopBkMusic()
{
	for (SkyBkMusic::const_iterator it = _bkMusic.begin(); it != _bkMusic.end(); ++it)
	{
		(*it)->stop();
		removeSource(*it);
	}
	_bkMusic.clear();
}

void SkyDefaultOpenALContext::pauseBkMusic()
{
	for (SkyBkMusic::const_iterator it = _bkMusic.begin(); it != _bkMusic.end(); ++it)
		(*it)->pause();
}

void SkyDefaultOpenALContext::resumeBkMusic()
{
	for (SkyBkMusic::const_iterator it = _bkMusic.begin(); it != _bkMusic.end(); ++it)
		(*it)->resume();
}

////////////////////////////////////////////////////////
// protected interface

void SkyDefaultOpenALContext::update(float dt)
{
	for (SkyBkMusic::const_iterator it = _bkMusic.begin(); it != _bkMusic.end(); ++it)
		(*it)->update(dt);
		
}

int SkyDefaultOpenALContext::playStaticSound(SkySoundSourceStatic* source, SkyOpenALBuffer* fileBuffer, bool loop, float gain, float pitch)
{
	source->setSoundID(generateSoundID());
	SkyOpenALBuffer* oldFileBuffer = source->currentBuffer();
	//Remove old source link
	if (oldFileBuffer != NULL)
	{
		std::pair<SkyConnectedEffects::const_iterator, SkyConnectedEffects::const_iterator> ret = _connectedEffects.equal_range(oldFileBuffer->bufferID());
		for (SkyConnectedEffects::const_iterator it = ret.first; it != ret.second; ++it)
			if (it->second == source)
			{
				_connectedEffects.erase(it);
				break;
			}
	}
	//Create new link
	_connectedEffects.insert(SkyConnectedEffects::value_type(fileBuffer->bufferID(), source));
	//Play sound
	source->setSoundID(generateSoundID());
	source->setBuffer(fileBuffer);
	((SkySSTagDefault*)source->tag())->_realGain = gain;
	source->setGain(s_effectsVolume*gain);
	source->setPitch(pitch);
	source->play();
	source->setIsLooping(loop);
	return source->soundID();
}

SkySSTagDefault* SkyDefaultOpenALContext::createTag(float realGain)
{
	return new SkySSTagDefault(realGain);
}


} // namespace sky
